#ifndef GMAP_MAPREDUCE_RANGE_
#define GMAP_MAPREDUCE_RANGE_

#include <cassert>
#include <initializer_list>

template <typename T = int>
struct dim_fancy
{
    T begin;
    T end;

    T step;

    dim_fancy(T _begin, T _end, T _step) :
        begin(_begin),
        end(_end),
        step(_step)
    {
    }

    dim_fancy &operator=(const dim_fancy &d)
    {
        begin = d.begin;
        end   = d.end;
        step  = d.step;
    }
};

template <typename T = int>
struct dim
{
    T begin;
    T end;

    static const T step = 1;

    dim() :
        begin(0),
        end(0)
    {
    }

    dim(T _begin, T _end) :
        begin(_begin),
        end(_end)
    {
    }

    dim(T _end) :
        begin(0),
        end(_end)
    {
    }

    dim &operator=(const dim &d)
    {
        if (&d != this) {
            begin = d.begin;
            end   = d.end;
        }

        return *this;
    }
};

typedef dim_fancy<int> dim_int;

template <typename T>
struct iterator {
    const dim_fancy<T> &dim_;
    T t_;

    iterator(const dim_fancy<T> &dim_fancy) :
        dim_(dim_fancy),
        t_(dim_fancy.begin)
    {
    }

    iterator &operator++()
    {
        t_ += dim_.step;
    }

    operator bool() const
    {
        return t_ < dim_.end;
    }

    T operator*() const
    {
        return t_;
    }
};

template <unsigned _NDims, typename T = int>
struct range_fancy {
public:
    typedef T type;
    typedef dim_fancy<T> dim_type;
    const static unsigned NDims = _NDims;

private:
    dim_type dims_[NDims];

    inline
    void
    init_range(const dim_type &d)
    {
        dims_[NDims - 1] = d;
    }

    template <typename... Dims>
    inline
    void
    init_range(const dim_type &d, const Dims &... dims)
    {
        dims_[NDims - sizeof...(Dims) - 1] = d;
        init_range(dims...);
    }
public:
    template <typename... Dims>
    inline
    range_fancy(const Dims &... dims)
    {
        static_assert(NDims == sizeof...(Dims), "Dimensions should match");

        init_range(dims...);
    }

#if 0
    range_fancy(std::initializer_list<T> list)
    {
        assert(NDims == list.size());

        unsigned idx = 0;
        for (auto d : list) {
            dims_[idx++] = d;
        }
    }
#endif

    inline
    dim_type get_dim(unsigned ndim)
    {
        return dims_[ndim];
    }
};

template <unsigned _NDims, typename T = int>
struct range {

public:
    typedef T type;
    typedef dim<T> dim_type;
    const static unsigned NDims = _NDims;

private:
    dim_type dims_[NDims];

    void
    init_range(const dim_type &d)
    {
        dims_[NDims - 1] = d;
    }

    template <typename... Dims>
    void
    init_range(const dim_type &d, const Dims &... dims)
    {
        dims_[NDims - sizeof...(Dims) - 1] = d;
        init_range(dims...);
    }
public:
    template <typename... Dims>
    range(const Dims &... dims)
    {
        static_assert(NDims == sizeof...(Dims), "Dimensions should match");

        init_range(dims...);
    }

#if 0
    range(std::initializer_list<T> list)
    {
        assert(NDims == list.size());

        unsigned idx = 0;
        for (auto d : list) {
            dims_[idx++] = d;
        }
    }
#endif

    const dim_type &get_dim(unsigned ndim) const
    {
        return dims_[ndim];
    }
};

template <typename... Dims>
range<sizeof...(Dims)>
make_range(const Dims &... dims)
{
    return range<sizeof...(Dims)>(dims...);
}

template <typename T, size_t Size>
range<std::rank<T[Size]>::value, typename std::remove_extent<T>::type>
make_range()
{
    return range<std::rank<T[Size]>::value, typename std::remove_extent<T>::type>
           (Size, std::extent<T[Size]>::value);
}

template <typename... Dims>
range<sizeof...(Dims)>
make_seq_range(const Dims &... dims)
{
    return range<sizeof...(Dims)>(dims...);
}

template <typename T, size_t Size>
range<std::rank<T[Size]>::value, typename std::remove_extent<T>::type>
make_seq_range()
{
    return range<std::rank<T[Size]>::value, typename std::remove_extent<T>::type>
           (Size, std::extent<T[Size]>::value);
}

#endif

/* vim:set ft=cpp backspace=2 tabstop=4 shiftwidth=4 textwidth=120 foldmethod=marker expandtab: */
